package generate

import (
	"database/sql"
	"fmt"
	"gitee.com/tainanleya/go_generate_tool/utils"
	"log"
	"os"
	"strings"
	"text/template"
)

type MySqlExecutor struct {
}

func (m *MySqlExecutor) GetTableMetadata(db *sql.DB, dbName string) ([]Table, error) {
	query := `
        SELECT table_name
        FROM information_schema.tables
        WHERE table_schema = ?;
    `
	rows, err := db.Query(query, dbName)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var tables []Table
	for rows.Next() {
		var tableName string
		if err := rows.Scan(&tableName); err != nil {
			return nil, err
		}

		columns, err := m.GetTableColumns(tableName, db, dbName)
		if err != nil {
			return nil, err
		}
		t := Table{Name: tableName, Columns: columns}
		err = m.TableHandle(&t)
		if err != nil {
			return nil, err
		}
		tables = append(tables, t)
	}

	return tables, nil
}

func (m *MySqlExecutor) GetTableColumns(tableName string, db *sql.DB, dbName string) ([]Column, error) {
	query := `SELECT column_name, data_type ,column_comment ,column_key
        FROM information_schema.columns
        WHERE table_schema = ? AND table_name = ?`
	rows, err := db.Query(query, dbName, tableName)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var columns []Column
	for rows.Next() {
		var columnName, dataType, comment, key string
		if err := rows.Scan(&columnName, &dataType, &comment, &key); err != nil {
			return nil, err
		}
		col := Column{Name: columnName, Type: dataType, Comment: comment, Key: key}
		err = m.ColumnMetaHandle(&col)
		if err != nil {
			return nil, err
		}
		columns = append(columns, col)
	}

	return columns, nil
}

func (m *MySqlExecutor) TableHandle(table *Table) error {
	// 处理名称 第一个字母大写 如果是下划线就转驼峰
	var templateName string
	for i := 0; i < len(table.Name); i++ {
		// 如果是第一个字符 并且是小写的那么将他转为大写
		if i == 0 && table.Name[i] >= 'a' && table.Name[i] <= 'z' {
			templateName = templateName + string(table.Name[i]-32)
			continue
		}
		if table.Name[i] == '_' && i+1 < len(table.Name) {
			// 如果是下划线那么就取下一个字母的大写字母
			i++
			templateName = templateName + string(table.Name[i]-32)
			continue
		}
		// 其他情况直接赋值了
		templateName = templateName + string(table.Name[i])
	}
	if len(templateName) == 0 {
		return nil
	}
	table.ModelName = templateName
	// 第一个字母转为小写
	table.PrivateName = string(templateName[0]+32) + templateName[1:]
	return nil
}

func (m *MySqlExecutor) ColumnMetaHandle(column *Column) error {
	// 处理名称 第一个字母大写 如果是下划线就转驼峰
	var templateName string
	for i := 0; i < len(column.Name); i++ {
		// 如果是第一个字符 并且是小写的那么将他转为大写
		if i == 0 && column.Name[i] >= 'a' && column.Name[i] <= 'z' {
			templateName = templateName + string(column.Name[i]-32)
			continue
		}
		if column.Name[i] == '_' && i+1 < len(column.Name) {
			// 如果是下划线那么就取下一个字母的大写字母
			i++
			templateName = templateName + string(column.Name[i]-32)
			continue
		}
		// 其他情况直接赋值了
		templateName = templateName + string(column.Name[i])
	}
	column.ModelName = templateName
	column.ModelComment = " // " + column.Comment
	column.ModelTag = "`db:\"" + column.Name + "\"`"
	column.DomainTag = "`json:\"" + column.Name + "\" form:\"" + column.Name + "\"`"
	// 获取到主键的信息
	if column.Key == "PRI" {
		column.ModelTag = "`db:\"" + column.Name + "\" fieldtag:\"pk\"`"
	}
	// 处理类型
	switch column.Type {
	case "varchar":
		column.ModelType = "sql.NullString"
		column.DomainType = "string"
	case "text":
		column.ModelType = "sql.NullString"
		column.DomainType = "string"
	case "datetime":
		column.ModelType = "sql.NullTime"
		column.DomainType = "string"
	case "int":
		column.ModelType = "sql.NullInt32"
		column.DomainType = "int"
	case "tinyint":
		column.ModelType = "sql.NullInt16"
		column.DomainType = "int8"
	case "timestamp":
		column.ModelType = "sql.NullTime"
		column.DomainType = "string"
	case "bigint":
		column.ModelType = "sql.NullInt64"
		column.DomainType = "int64"
	default:
		panic(fmt.Sprintf("mysql类型[%s]未寻找到", column.Type))
	}
	return nil
}

func (m *MySqlExecutor) GenerateModel(table *Table) error {
	err := m.GenerateTemplate(table, "./generate_code/model/"+table.ModelName+"Model.go", modelTemplate)
	if err != nil {
		return err
	}
	return nil
}

func (m *MySqlExecutor) GenerateDomain(table *Table) error {
	err := m.GenerateTemplate(table, "./generate_code/domain/dto/"+table.ModelName+"DTO.go", dtoTemplate)
	if err != nil {
		return err
	}
	err = m.GenerateTemplate(table, "./generate_code/domain/vo/"+table.ModelName+"Vo.go", voTemplate)
	if err != nil {
		return err
	}
	return nil
}

func (m *MySqlExecutor) GenerateService(table *Table) error {
	err := m.GenerateTemplate(table, "./generate_code/service/"+table.ModelName+"Service.go", serviceTemplate)
	if err != nil {
		return err
	}
	return nil
}

func (m *MySqlExecutor) GenerateController(table *Table) error {
	err := m.GenerateTemplate(table, "./generate_code/controller/"+table.ModelName+"Controller.go", controllerTemplate)
	if err != nil {
		return err
	}
	return nil
}

func (m *MySqlExecutor) GenerateTemplate(table *Table, filePath string, temp string) error {
	// Create a template and parse it
	tmpl, err := template.New("template").Funcs(template.
		FuncMap{
		"getColumns":   func(t *Table) []Column { return t.Columns },
		"isPrimaryKey": func(key string) bool { return strings.EqualFold("PRI", key) },
	}).
		Parse(temp)
	if err != nil {
		log.Fatal(err)
	}
	err = utils.CreateDirIfNotExist(filePath[:strings.LastIndex(filePath, "/")])
	if err != nil {
		return err
	}
	err = utils.DeleteFile(filePath)
	if err != nil {
		return err
	}
	// Create a file to write the generated code
	outputFile, err := os.Create(filePath)
	if err != nil {
		log.Fatal(err)
	}
	defer outputFile.Close()

	// Execute the template and write the generated code to the file
	err = tmpl.Execute(outputFile, table)
	if err != nil {
		log.Fatal(err)
	}
	return nil
}
